home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / LIBRARY / MCQUAY1 / QSORT.PAS < prev    next >
Pascal/Delphi Source File  |  1991-11-12  |  16KB  |  423 lines

  1.  
  2.  
  3.  
  4. (****************************************************************************
  5.  A GENERIC QUICK SORT UNIT
  6.  version 5.0 and 5.1
  7.  1/1/90
  8.  ray quay
  9.  
  10.  Copyright 1990 McQuay Technologies
  11.  Released into the Public Domain
  12.  However, please give credit where credit is due !
  13.  
  14.  *****************************************************************************
  15.  
  16.   ------- SYNTAX -------------------------------------------------------------
  17.   This is a generic quick sort routine that works similar to UNIX C's qsort
  18.   function.
  19.  
  20.   procedure quick_sort( var sort_array ;         { untyped var reference }
  21.                                             rec_size, n_recs: word;
  22.                                             compare_function : compare_func);  { procedure-type
  23.                                                              parameter     }
  24.   Parameters
  25.       sort_array - this is the structure that will be sorted, can be of
  26.                    any data type (need not be an array).  This is technically
  27.                    a reference to the "top" of the data to be sorted.  The
  28.                    data however, must be located continuously in memory
  29.                    from the top to the bottom of the data area.
  30.       rec_size   - this is the size in bytes of each record to be sorted.
  31.       n_recs     - this is the number of records referenced by sort_array.
  32.                    Techinically, it is the number of records of rec_size from
  33.                    the "top" of the data referenced to the bottom of the data
  34.                    to be sorted.
  35.       compare_function - this is a procedure-type parameter which is a
  36.                    function called during the sort (see below).
  37.                    Your compare function must be declared FAR with the
  38.                    {$F+} compiler switch.
  39.   ------- USAGE --------------------------------------------------------------
  40.   This routine can sort an array of any type (or any structure) whose size is
  41.   less than 64K bytes.  Size limitations can be overcome by creating either
  42.   an index array, or an array of pointers, and sort the index or pointer
  43.   array.  You have control over the conditions of the sort, via
  44.   the compare_address function.  You must create a function which accepts
  45.   two var parameters, and returns an integer.  The rules for the value returned
  46.   by the function are the same as that for the UNIX C qsort function.  The
  47.   following is an example of a function to sort an array of integers.
  48.  
  49.   {$F+}
  50.   function compare (var v1,v2 : integer) :integer;
  51.   begin
  52.     if  v1 < v2 then compare := -1 else
  53.       if v1 > v2 then compare := 1 else
  54.         compare := 0;
  55.   end;
  56.     {$F-}  add this if you want it off
  57.  
  58.   The actual name of the function can be anything, it need only accept two
  59.   var parameters and return an integer as above.  The address of your compare
  60.   function is passed via the compare_function procedure-type parameter.
  61.  
  62.   There are five predefined compare functions available to you, these are :
  63.  
  64.     compare_word        Which is used to compare values of a word array
  65.       compare_longint     Which is used to compare values of a longint array
  66.       compare_int         Which is used to compare values of an integer array
  67.       compare_real        Which is used to compare values of a real array
  68.       compare_byte        Which is used to compare values of a byte array
  69.  
  70.   You can use these instead of building your own routine.  Here is an example:
  71.     uses qsort;
  72.     const
  73.       ArraySize = 10;
  74.     var
  75.           sort_array : array[1..ArraySize] of word;
  76.       i:word;
  77.     begin
  78.       for i:=1 to ArraySize do
  79.                 sort_array[i] := random(i);
  80.             quick_sort(sort_array,sizeof(sort_array[1],ArraySize,compare_word);
  81.             for i:=1 to ArraySize do
  82.                 writeln(i:4,sort_array[i]:3);
  83.         end.
  84.  
  85.   ------- ERRORS -------------------------------------------------------------
  86.   QSORT version 5.0 has no error checking.  QSORT 5.1 has minimal error
  87.   checking.  You can compile Version 5.0 by deleting the line
  88.     {$DEFINE DEBUGCODE }
  89.   and compile version 5.1 by leaving this line in. In version 5.1  a check
  90.   is made to make sure the reference to the array is not NIL, a check is
  91.   made to make sure enough Heap is available for temporary storage.
  92.  
  93.   There is no type checking going on here so it is entirely up to you to make
  94.   sure your function evaluates the type of variable that will be passed to it,
  95.   that you pass to qsort the correct function. Turbo will not let you pass
  96.   a function with the wrong structure, still it is up to you to be sure it
  97.   is doing the correct comparison.  The only real damage that can occur is if
  98.   you give it an incorrect record size.  (Fatal errors result if you pass
  99.   a record size that is to large).
  100.  
  101.   Version 5.1 has several levels of error checking available.  First, the
  102.   QSortResult function can be used to test if the sort was successful.  If
  103.   QSortResult returns a 0, then there was no error detected, a nonzero value
  104.   indicates an error, as follows:
  105.                             Full       Full       Turbo
  106.                              Hex      Decimal   Error Code
  107.                             -----     -------   ----------
  108.       MemoryOverFlowError = $10CB   -  4299   -    203
  109.       BadFunctionPointer  = $112C   -  4396   -    300
  110.       BadArrayVarPointer  = $10CC   -  4300   -    204
  111.  
  112.  
  113.   Qsort version 5.1 also uses the FRTE runtime error unit to provide an
  114.   advanced level of debugging error trapping.  Assigning TRUE to QSortDebug
  115.   will cause Turbo's runtime error support system to trap all errors.
  116.   This will display the error code and place the cursor at the call to Qsort()
  117.   that caused the error.  Removing the line $DEFINE DEBUGCODE will compile
  118.   to version 5.0, which has no error trapping.
  119.  
  120.   ------- STATS --------------------------------------------------------------
  121.   Version 5.1 compiles to 2K of TPU code (not counting the FRTE unit)
  122.   with all compiler switches off, and uses 4 bytes of the data segment.
  123.   Version 5.0 compiles to 1.8K  of TPU code uses no space in the data segment.
  124.  
  125.   Speed wise, on an 8mhz system Qsort can sort an array of word with 1000
  126.   elements in 1.5 seconds and on a 25mhz cache system sort about 5000 words
  127.   in the same amount of time.  This is not ultra fast, but it is fairly fast
  128.   for a general utility sort routine.
  129.  
  130.   ------- BACKGROUND ---------------------------------------------------------
  131.   The quick sort algorithm is a divide and conquer startegy.  It recursively
  132.   divides the array into smaller arrays, ordering the size to left the smaller
  133.   and to the right the larger as it goes.  Yes, the following code is sparsely
  134.   commented and it looks like greek.  If you want to understand the quick sort,
  135.   and do not now understand it, I suggest you get a good advanced pascal book.
  136.   Most will explain quick sort.  I am not going to attempt to do so here,
  137.   suffice this works (I HOPE!).  This routine is adapted from
  138.  
  139.    Sgonina, Warner; TURBO PASCAL TRICKS AND TIPS; Abacus Software; 1985.
  140.    and
  141.    Duntemann; COMPLETE TURBO PASCAL; Scott, Foresman and Company; 1986.
  142.  
  143.    COMPLETE TURBO has a better explanation, TRICKS AND TIPS has a better
  144.    routine.
  145.  
  146.    This routine uses pointers to reference all the data in
  147.    the array or data structure to be sorted.  This is what makes it a
  148.    generic routine.  It also uses the TPASCAL procedure getmem() to allocate
  149.    enough temporary storage for the swap.  If you are using a different memory
  150.    management scheme, replace this call with one of your own.
  151.  
  152.    Yes, there is most definitely a time factor sacrifice for using
  153.    pointers, it increases the sort time by about a factor of 4.  Ahh,
  154.    the price you pay.  This routine is also slighty larger than a routine
  155.    specifically designed to sort a particualar data array, but will take up
  156.    less code than would be required to write multiple sort routines, one for
  157.    each type of data structure.
  158.  
  159.    Stack checking has been turned off for all but the recursive calls.  At
  160.      the most, 20 bytes are needed for all other calls.  The stack is recovered
  161.      prior to each recursive call, so it is not likely you will run into any
  162.      stack problems with out the stack check catching it.  If you are having
  163.      stack problems, recompile this unit with stack checking turned back on.
  164.  
  165.    Comments or bugs will be appreciated,    maybe.
  166. *)
  167.  
  168. unit qsort;
  169. { Compiler Switches}
  170.   {$A+,B-,D-,E-,F-,I-,L-,N-,O-,R-,S-,V-}
  171.   {$L swpdata4.obj }
  172.   {$DEFINE DEBUGCODE }
  173.  
  174. interface
  175.   type
  176.     compare_func = function(var v1,v2 ) :integer;
  177.  
  178.   procedure quick_sort(  var sort_array ;   { untyped var reference }
  179.                                         rec_size, n_recs: word;
  180.                                         compare_function : compare_func);
  181.  
  182.   function F_compare_word    (var v1 ; var v2 ) : integer;
  183.     { Used to compare values of a word array     }
  184.   function F_compare_int     (var v1 ; var v2 ): integer;
  185.     { Used to compare values of an integer array }
  186.   function F_compare_longint (var v1 ; var v2 ) : integer;
  187.     { Used to compare values of a longint array  }
  188.   function F_compare_byte    (var v1 ; var v2  ) : integer;
  189.     { Used to compare values of a byte array     }
  190.   function F_compare_real    (var v1 ; var v2 ) : integer;
  191.     { Used to compare values of a real array     }
  192.  
  193.   {$IFDEF DEBUGCODE }
  194.  
  195.   function QSortResult:word;
  196.   const
  197.     QSortDebug : boolean = false;
  198.   {$ENDIF }
  199.  
  200. implementation
  201.   {$IFDEF DEBUGCODE }
  202.   uses frte;
  203.  
  204.   const
  205.     MemoryOverFlowError = $10CB;
  206.     BadFunctionPointer  = $112C;
  207.     BadArrayVarPointer  = $10CC;
  208.   {$ENDIF }
  209.  
  210.   { Compare Functions }
  211.   {$F+}
  212.   {------------------------------------------------}
  213.   function F_compare_int;
  214.   var
  215.     va : integer absolute v1;
  216.     vb : integer absolute v2;
  217.   begin
  218.     F_compare_int := va-vb;
  219.   end;
  220.   {------------------------------------------------}
  221.   function F_compare_word ;
  222.   var
  223.     va : word absolute v1;
  224.     vb : word absolute v2;
  225.   begin
  226.     F_compare_word := integer(v1)-integer(v2);
  227.   end;
  228.   {------------------------------------------------}
  229.   function F_compare_longint;
  230.   var
  231.     va : longint absolute v1;
  232.     vb : longint absolute v2;
  233.   begin
  234.     F_compare_longint := va-vb;
  235.   end;
  236.   {------------------------------------------------}
  237.   function F_compare_byte;
  238.   var
  239.     va : byte absolute v1;
  240.     vb : byte absolute v2;
  241.   begin
  242.     F_compare_byte := integer(va)-integer(vb);
  243.   end;
  244.   {------------------------------------------------}
  245.   function F_compare_real;
  246.   var
  247.     temp : real;
  248.     va : real absolute v1;
  249.     vb : real absolute v2;
  250.   begin
  251.      temp := va - vb;
  252.      if temp <0 then F_compare_real := -1
  253.        else if temp >0 then F_compare_real := 1
  254.          else F_compare_real := 0;
  255.   end;
  256.   {------------------------------------------------}
  257.  
  258.   {$F+}
  259.   (*
  260.   {--------------------------------------------------------------------------}
  261.   function call_compare (v1seg,v1ofs:word; var v2; the_call : compare_func):integer;
  262.  
  263.   { This little routine is used to call your compare function.  It also
  264.     makes a good boiler plate for similar uses of calling a function by
  265.     reference.  The call passes the offset or address of the function to be
  266.     called via the integer parameter the_call.  }
  267.  
  268.   begin
  269.     inline
  270.      (
  271.     { Return Turbo to state right after call }
  272.       $8B/$E5/               { mov SP,BP }
  273.       $5D/                   { pop BP }
  274.  
  275.     { Now pop off the return address, pop of the function reference,
  276.       and reverse their order on the stack, then do a far ret, which
  277.       will return to the function reference, leaving the stack as if
  278.       it went there in the first place.  Was that clear ?}
  279.       $58/                   { pop  AX   ; return address }
  280.       $5B/                   { pop  BX                    }
  281.       $59/                   { pop  CX   ; Function       }
  282.       $5A/                   { pop  DX   ; reference      }
  283.       $53/                   { push BX   ; push back      }
  284.       $50/                   { push AX   ; return Address }
  285.       $52/                   { push DX   ; push back      }
  286.       $51/                   { push CX   ; function ref.  }
  287.       $CB);                  { retf      ; do a far return}
  288.    end;                      {             to function ref}
  289.   *)
  290.   {$F-}
  291.   procedure swapdata (v1seg,v1ofs:word; v2seg,v2ofs:word; var temp; size : integer); external;
  292.  
  293.   { This is an assembly routine that swaps records of any length up to 64K.
  294.     See SWAPDATA.ASM for source code.
  295.     v1, v2, and temp are any variables of equal size, size is a value for the
  296.     size of these variables.  This is faster than using move()}
  297.  
  298.   {----------------------------------------------------------------------------}
  299. {$IFDEF DEBUGCODE}
  300.   const
  301.     TheResult : word = 0;
  302.   function QSortResult:word;
  303.    begin
  304.      QSortResult := TheResult; TheResult := 0;
  305.    end;
  306.  
  307.   {$F+}
  308.   procedure QsortError(ErrorCode:word);
  309.   begin
  310.     if QSortDebug then
  311.       Frterror(Find_Far_Caller(1),ErrorCode)
  312.     else
  313.       TheResult := ErrorCode;
  314.   end;
  315.   {$F-}
  316. {$ENDIF}
  317.   {----------------------------------------------------------------------------}
  318.   procedure quick_sort;
  319.   {
  320.   (var sort_array ; rec_size, n_recs: word; compare_address : pointer);
  321.   }
  322.  
  323.   var
  324.    aseg : integer;
  325.    temp2 : ^integer;
  326.    temp1 : ^integer;
  327.    temp3 : ^integer;
  328.    temp4 : ^integer;
  329.  
  330.  
  331.   {----------------------------------------------------------------------------}
  332.   procedure sort_1 (left,right: word);
  333.  
  334.   { This is the recursive part }
  335.   var
  336.    i, j : integer;
  337.  
  338.   begin
  339.     { temp1 is the mid point in the block passed to sort_1 }
  340.     i:=(right - left) shr 1;
  341.     move(mem[aseg:( i +(left - (i mod rec_size)))],temp1^,rec_size);
  342.  
  343.     i:=left;
  344.     j:=right;
  345.  
  346.     while i < j do
  347.       begin
  348.       { move i up to a value near the mid point value
  349.         and j down to a value near the mid point value }
  350.       temp3 := ptr(aseg,i);
  351.       while compare_function (temp3^,temp1^) < 0 do
  352.         begin
  353.         inc(i,rec_size);
  354.         temp3 := ptr(aseg,i);
  355.         end;
  356.       temp4 := ptr(aseg,j);
  357.       while compare_function (temp4^,temp1^) > 0 do
  358.         begin
  359.         dec(j,rec_size);
  360.         temp4 := ptr(aseg,j);
  361.         end;
  362.       { now swap em if i is still below j }
  363.       if i <= j then
  364.         begin
  365.         swapdata(aseg,i,aseg,j,temp2^,rec_size);
  366.         inc(i,rec_size);
  367.         dec(j,rec_size);
  368.         end
  369.       end;
  370.     {Ok now sort the outside blocks }
  371.     {$S+}
  372.     if left < j then sort_1(left,j);
  373.     if i < right then sort_1(i,right)
  374.     {$S-}
  375.     end;
  376.  
  377.   {********** MAIN CODE ***********}
  378.   begin
  379.     aseg := seg(sort_array);
  380.  
  381.     {$IFDEF DEBUGCODE }
  382.     { check to see if valid function pointer }
  383.     if @compare_function = nil then
  384.       begin
  385.       QsortError(BadFunctionPointer);
  386.       exit;
  387.       end;
  388.  
  389.     { Check to see if valid array pointer }
  390.     if aseg = 0 then
  391.       begin
  392.       QSorterror(BadArrayVarPointer);
  393.       exit;
  394.       end;
  395.  
  396.     { check to see if there is enough memory }
  397.     if memavail < (rec_size*2) + $40  then
  398.       begin
  399.       QSorterror(MemoryOverflowError);
  400.       exit;
  401.       end;
  402.     {$ENDIF}
  403.  
  404.     { Allocate Sapce on heap for Temp records }
  405.     getmem(temp1,rec_size);
  406.     getmem(temp2,rec_size);
  407.  
  408.     { OK if more than one record, Sort it }
  409.     if n_recs > 1 then
  410.       begin
  411.       sort_1(ofs(sort_array),(ofs(sort_array))+(n_recs-1)*rec_size);
  412.       end;
  413.  
  414.     { Unallocate heap space used }
  415.     freemem(temp1,rec_size);
  416.     freemem(temp2,rec_size);
  417.   end;
  418.  
  419.  
  420.   { --- INITIALIZATION ROUTINE ---- }
  421.   begin
  422.   end.
  423.